package com.cc.fsadbruninit

import android.Manifest
import android.annotation.SuppressLint
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.net.wifi.WifiManager
import android.os.Bundle
import android.os.Environment
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.isDigitsOnly
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject
import pub.devrel.easypermissions.EasyPermissions
import java.io.File
import java.io.IOException
import java.net.InetAddress
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.*


class MainActivity : AppCompatActivity(), View.OnClickListener {

    var start: Button? = null
    var stop: Button? = null
    var port: EditText? = null
    var fridaPort: EditText? = null
    var connect: TextView? = null

    var fdName: EditText? = null

    var initBtn: Button? = null

    var hasRoot: Boolean = false

    var client = OkHttpClient()

    var receiver: BroadcastReceiver? = null

    var debugBtn: Button? = null

    private val perms = arrayOf(
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.ACCESS_WIFI_STATE
    )

    private fun getPermissions() {
        if (!EasyPermissions.hasPermissions(this, *perms)) {
            EasyPermissions.requestPermissions(
                this, "", 0,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
        }
        if (!EasyPermissions.hasPermissions(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            EasyPermissions.requestPermissions(
                this, "", 0,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            )
        }
        if (!EasyPermissions.hasPermissions(this, Manifest.permission.ACCESS_WIFI_STATE)) {
            EasyPermissions.requestPermissions(
                this, "", 0,
                Manifest.permission.ACCESS_WIFI_STATE
            )
        }

    }

    @SuppressLint("SetTextI18n")
    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.start -> {
                start()
            }
            R.id.stop -> {
                stop()
            }
            R.id.init -> {
                // 初始化 先判断是否存在文件
                initBtn!!.isEnabled = false
                runInit()
            }
            R.id.debug -> {
                setGlobalDebugger()
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        start = findViewById(R.id.start)
        stop = findViewById(R.id.stop)
        port = findViewById(R.id.port)
        connect = findViewById(R.id.connect_hint)
        fridaPort = findViewById(R.id.fridaPort)
        fdName = findViewById(R.id.fridaHit)
        initBtn = findViewById(R.id.init)
        debugBtn = findViewById(R.id.debug)

        start!!.setOnClickListener(this)
        stop!!.setOnClickListener(this)
        initBtn!!.setOnClickListener(this)
        debugBtn!!.setOnClickListener(this)

        getPermissions()
        hasRoot = hasSu()

        val uri = intent.data
        if (uri != null) {
            val it = uri.queryParameterNames.iterator()
            while (it.hasNext()) {
                val k = it.next()
                t(k)
                when (k) {
                    "adb_port" -> {
                        port!!.setText(uri.getQueryParameter(k))
                    }
                    "frida_name" -> {
                        fdName!!.setText(uri.getQueryParameter(k))
                    }
                    "frida_port" -> {
                        fridaPort!!.setText(uri.getQueryParameter(k))
                    }
                    "op" -> {
                        when (uri.getQueryParameter(k)) {
                            "start" -> {
                                start()
                            }
                            "stop" -> {
                                stop()
                            }
                            else -> {
                                t("初始操作认证成功")
                            }
                        }
                    }
                }
            }
        }
    }

    @SuppressLint("SetTextI18n")
    private fun start() {
        val f = Integer.valueOf(fridaPort!!.text.toString())
        if (f > 65535 || f < 1) {
            t("frida端口必须在 0 ~ 65565")
            return
        }
        val p = Integer.valueOf(port!!.text.toString())
        if (p > 65535 || p < 1) {
            t("adb端口必须在 0 ~ 65565")
            return
        }
        start!!.isEnabled = false
        stop!!.isEnabled = true
        setAdb(p)
        setFrida(f, true)
        connect!!.text =
            "adb电脑端连接: \n $wifiAddress:${port!!.text} \n frida 连接 \n $wifiAddress:${fridaPort!!.text}"
    }


    private fun stop() {
        setAdb(-1)
        setFrida(0, false)
        start!!.isEnabled = true
        stop!!.isEnabled = false
    }

    private fun setGlobalDebugger() {
        if (hasRoot) {
            try {
                Runtime.getRuntime().exec("su -c magisk resetprop ro.debuggable 1")
                Runtime.getRuntime().exec("su -c stop;start")
            } catch (e: IOException) {
                t("启用全局调试状态出错")
            }
        }
    }

    private val wifiAddress: String
        get() {
            val wifiMgr = (applicationContext.getSystemService(WIFI_SERVICE) as WifiManager)
            val wifiInfo = wifiMgr.connectionInfo
            return if (wifiInfo != null) {
                val ipAddressInt = wifiInfo.ipAddress
                val ipAddress = InetAddress.getByAddress(
                    ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ipAddressInt)
                        .array()
                )
                    .getHostAddress()
                ipAddress
            } else {
                ""
            }
        }

    // 设置adb
    private fun setAdb(enable: Int) {
        if (hasRoot) {
            try {
                Runtime.getRuntime().exec("su -c setprop service.adb.tcp.port $enable")
                Runtime.getRuntime().exec("su -c stop adbd")
                Runtime.getRuntime().exec("su -c start adbd")
            } catch (e: IOException) {
                t("启动adb错误")
            }
        }
    }

    private fun t(text: String) {
        Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
    }


    // 是否存在超级管理员权限
    private fun hasSu(): Boolean {
        return try {
            Runtime.getRuntime().exec("su")
            val file = File("/data/")
            file.canWrite()
            true
        } catch (e: IOException) {
            t("请给予root权限后重试")
            false
        }
    }


    // 设置frida
    private fun setFrida(p: Int, r: Boolean) {
        if (hasRoot) {
            try {
                if (r) {
                    Runtime.getRuntime().exec("su -c chmod 777 /data/local/tmp/${fdName!!.text}")
                    Runtime.getRuntime()
                        .exec("su -c ./data/local/tmp/${fdName!!.text} -l 0.0.0.0:$p &")
                } else {
                    Runtime.getRuntime()
                        .exec("su -c ps -e | grep ${fdName!!.text} | awk '{print $2}' | xargs kill -9")
                }
            } catch (e: IOException) {
                if (e.message?.isDigitsOnly()!!) {
                    t("frida ${fdName!!.text} 变更成功${r}${e}")
                } else {
                    t("frida ${fdName!!.text} 变更错误${r}${e}")

                }
            }
        }

    }


    // 进行初始化操作
    private fun runInit() {


        if (hasRoot) {
            // 先删除原来的文件
//            Runtime.getRuntime().exec("su -c rm /data/local/tmp/${fdName!!.text}")
            // 获取当前cpu架构
            val device = execCmd("su -c getprop ro.product.cpu.abi | awk '{print \$1}'")
            var abi = "arm"
            if (device != null) {
                if (device.startsWith("arm64")) {
                    abi = "arm64"
                }
            }
            Thread(Runnable {
                //请求网络
                val lastTag = getLastTag()
                t("获取到最新版本 $lastTag")

                val frida = "frida-server-$lastTag-android-$abi.xz"
                val url = "https://github.com/frida/frida/releases/download/${lastTag}/$frida"

                val dManager: DownloadManager =
                    getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
                val uri: Uri = Uri.parse(url)
                val request: DownloadManager.Request = DownloadManager.Request(uri)

                val savePath =
                    getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/" + frida
                // 设置下载路径和文件名
                request.setDestinationInExternalFilesDir(
                    this,
                    Environment.DIRECTORY_DOWNLOADS,
                    frida
                )
                request.setDescription("frida 下载")
                request.setTitle(frida)
                request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)

                t("开启下载")


                // 获取此次下载的ID
                val refernece = dManager.enqueue(request)
                // 注册广播接收器，当下载完成时自动安装
                val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
                receiver = object : BroadcastReceiver() {
                    override fun onReceive(context: Context?, intent: Intent?) {
                        println("onReceive!!!")
                        val myDwonloadID =
                            intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
                        if (refernece == myDwonloadID) {
                            val downloadFileUri = dManager.getUriForDownloadedFile(refernece)
                            if (downloadFileUri != null) {
                                println("获取到下载的文件")
                                // todo:需要解决xz文件解压缩的问题
//                                moveFile(savePath, "/data/local/tmp/${fdName!!.text}")
                            }
                        }
                    }
                }
                registerReceiver(receiver, filter)

            }).start()

            initBtn!!.isEnabled = true


        }

    }


    @Throws(IOException::class)
    fun execCmd(cmd: String?): String? {
        val s =
            Scanner(Runtime.getRuntime().exec(cmd).inputStream)
                .useDelimiter("\\A")
        return if (s.hasNext()) s.next() else ""
    }

    @Throws(IOException::class)
    fun getLastTag(): String {
        val request: Request = Request.Builder()
            .url("https://api.github.com/repos/frida/frida/releases/latest")
            .build()
        client.newCall(request).execute().use { response ->
            val tagJson = JSONObject(response.body!!.string())
            return tagJson.getString("tag_name")
        }
    }

    // 移动文件
    fun moveFile(filePath: String, savePath: String) {
        if (hasRoot) {
            try {
                Runtime.getRuntime().exec("su -c tar xvjf $filePath")
                Runtime.getRuntime().exec("su -c move $filePath $savePath")
            } catch (e: IOException) {
                t("移动文件失败")
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        if (receiver != null) {
            unregisterReceiver(receiver)
        }
    }

}
